﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using CodeBuilder2.BLL.Connection;
using CodeBuilder2.Model;
using CodeBuilder2.BLL.Schema;
using CodeBuilder2.Model.Enumeration;
using System.Threading;
using System.ComponentModel;
using CodeBuilder2.Model.Constant;
using System.Windows.Threading;
using CodeBuilder2.View.FloatWindow;

namespace CodeBuilder2.View.CB_UserControl.Content
{
    /// <summary>
    /// DataBaseContent.xaml 的交互逻辑
    /// </summary>
    public partial class DataBaseContent : UserControl
    {
        ServerBLL sBll = new ServerBLL();
        DataBaseBLL dbBll = new DataBaseBLL();

        public DataBaseContent()
        {
            InitializeComponent();
            InitTreeView();
        }

        /// <summary>
        /// 初始化 TreeView 控件
        /// </summary>
        void InitTreeView()
        {

            List<string> names = ConnectionHelper.GetConnectedServersName();
            // 遍历所有服务器名称，将这些服务器名称作为 TreeView 的一级节点增加到树上
            // 同时为每个服务器节点增加相应的子节点
            foreach (var name in names)
            {
                FillTreeNodes(name);
            }
        }

        /// <summary>
        /// 分别在 TreeView 控件上填充服务器、系统数据库（虚拟目录）、数据库节点
        /// </summary>
        /// <param name="serverName"></param>
        void FillTreeNodes(string serverName)
        {
            List<string> dbNames = sBll.GetDataBasesName(serverName);
            // 表示每一个即将加入到树中的 TreeViewItem
            TreeViewItem tvi = null;            
            
            #region 填充服务器级节点

            if (dbNames != null)
            {
                tvi = new TreeViewItem();
                //tvi.Header = System.Net.Dns.GetHostEntry(serverName).HostName;
                tvi.Header = serverName;
                tvi.Tag = TreeViewItemTagType.Server;
                tvi.IsExpanded = true;
                tvInstance.Items.Add(tvi);
            }
            else
            {
                return;
            }

            #endregion

            // 表示每一个即将加入到树中的 TreeViewItem 的最近的一个父 TreeViewItem
            TreeViewItem tviParent = null;

            #region 如果有系统数据库存在，则建立一个虚拟目录已囊括这些系统数据库（是否有必要判断，或当前判断逻辑是否严谨都是值得考虑，不过这里只是纯粹出于练习目的）

            if (dbNames.Contains("master"))
            {
                tvi = new TreeViewItem();
                tvi.Header = "系统数据库";
                tvi.Name = "sysdb";
                tviParent = tvInstance.Items[0] as TreeViewItem;
                tviParent.Items.Add(tvi);
            }

            #endregion

            // TODO: 这一部分应该还需要更改，现在的逻辑并不适用于多个服务器节点存在的情况
            tviParent = (tvInstance.Items[0] as TreeViewItem).Items[0] as TreeViewItem;
            TreeViewItem tviServer = tvInstance.Items[0] as TreeViewItem;

            #region 填充数据库级节点

            // 为该数据库级节点准备各个虚拟目录（表、视图、可编程性等）
            TreeViewItem tviTable = null;
            TreeViewItem tviView = null;
            TreeViewItem tviProgrammability = null;

            foreach (var item in dbNames)
            {
                tvi = new TreeViewItem();
                tvi.Tag = TreeViewItemTagType.DataBase;
                tvi.ContextMenuOpening += new ContextMenuEventHandler(tvi_ContextMenuOpening);
                // 订阅所有数据库级节点的展开事件
                tvi.Expanded += new RoutedEventHandler(tvi_Expanded);
                tvi.Header = item;
                if (item == "master" || item == "model" || item == "msdb" || item == "tempdb")
                {
                    //tvParent = (tvInstance.Items[0] as TreeViewItem).Items[0] as TreeViewItem;
                    tviParent.Items.Add(tvi);
                }
                else
                {
                    tviServer.Items.Add(tvi);
                }

                #region 将表、视图、可编程性三个虚拟节点增加到数据库节点下
                tviTable = new TreeViewItem();
                tviTable.Header = "表";
                tviTable.Tag = TreeViewItemTagType.Table;

                tviView = new TreeViewItem();
                tviView.Header = "视图";
                tviView.Tag = TreeViewItemTagType.View;

                tviProgrammability = new TreeViewItem();
                tviProgrammability.Header = "可编程性";
                tviProgrammability.Tag = TreeViewItemTagType.Programmabilities;


                tvi.Items.Add(tviTable);
                tvi.Items.Add(tviView);
                tvi.Items.Add(tviProgrammability);
                #endregion

                #region 为数据库级节点增加右键菜单

                //ContextMenu ctxtMenu = this.FindResource("databaseContextMenu") as ContextMenu;
                //tvi.ContextMenu = ctxtMenu;

                #endregion
            }

            #endregion
        }

        void FillExpandedTreeViewItem(object tviObj)
        {
            TreeViewItem tvi = tviObj as TreeViewItem;
            if (tvi == null)
                return;
            // 如果这里不暂停 500 毫秒（或许更少，总之需要一段时间），那么主线程的“（正在展开...）”不能确保每次都成功显示
            // 推测有两个原因：
            // 一是因为代码执行速度太快，人眼感觉不到后缀的增加和移除；
            // 二是因为本方法所在线程执行速度快于或者说是执行次序早于主线程。
            // 导致下面的 ThreadStart 委托先执行了（理由见下面的 Thread.Sleep(10000) 语句的注释）。
            // 不过上面的“先”可能不够准确，因为无法解释为什么先执行了委托部分代码，后执行主线程的加“（正在展开...）”后缀的代码
            // 却依然不能显示后缀。
            // 因为 ThreadStart 委托是被 tvi 对象的 Dispatcher 的 BeginInvoke 方法调用的，是运行在主线程（UI 线程）上的……
            // 所以，现在想来，应该是线程争抢之类的问题，而这个也是我现在所能想到的极限了，而且线程争抢究竟是如何影响到这里的，
            // 我现在也说不清……
            Thread.Sleep(500);
            ThreadStart start = delegate()
            {
                // 将树节点上的后缀状态字符串替换为空串，得到数据库名称
                string originName = tvi.Header.ToString().Replace(UIConstant.TREEVIEWITEM_EXPANDING, "");
                TreeViewItemTagType tvitt = GetTagType(tvi);
                // 即使在这里加上等待 10 秒的代码，但是如果在 ThreadStart 委托执行之前如果不调用那个 500 毫秒的暂停，
                // 则也不能保证正常显示“（正在展开...）”字样。
                //Thread.Sleep(10000); 
                switch (tvitt)
                {
                    case TreeViewItemTagType.DataBase:                        
                        FillDataBaseNode(tvi, originName);
                        break;
                    case TreeViewItemTagType.Table:
                        break;
                    case TreeViewItemTagType.View:
                        break;
                    case TreeViewItemTagType.Programmabilities:
                        break;
                    case TreeViewItemTagType.Functions:
                        break;
                    case TreeViewItemTagType.Procedures:
                        break;
                    case TreeViewItemTagType.Triggers:
                        break;
                    default:
                        break;
                }
                // 因为在调用本方法时，已经为树节点的名称加了后缀，
                // 又因为当前上下文恰好是通过被操作的数据节点对象的 Dispatcher 对象调用的
                // 所以，直接在这里还原树节点的本来名称
                tvi.Header = originName;
            };
            tvi.Dispatcher.BeginInvoke(start, DispatcherPriority.Normal);
            //RestoreHeaderText(tvi);
        }

        string sName;
        string dbName;
        void tvi_ContextMenuOpening(object sender, ContextMenuEventArgs e)
        {
            TreeViewItem tvi = sender as TreeViewItem;
            sName = this.GetServerName(tvi);
            dbName = tvi.Header.ToString();
        }

        /// <summary>
        /// 当展开树中的节点时调用此方法，该方法依据当前展开的节点
        /// 的 Tag 属性中所保存的 TreeViewItemTagType 值来填充相应
        /// 的数据到其子节点
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void tvi_Expanded(object sender, RoutedEventArgs e)
        {
            TreeViewItem tvi = e.Source as TreeViewItem;

            // 在当前展开的树节点名称后面追加一个表示正在展开树节点的后缀字符串
            tvi.Header += UIConstant.TREEVIEWITEM_EXPANDING;

            // 将费时的数据库操作放到独立线程中完成
            Thread t = new Thread(new ParameterizedThreadStart(this.FillExpandedTreeViewItem));
            t.Name = "FillExpandedTreeViewItem";
            t.Start(tvi);
        }

        void worker_DoWork(object sender, DoWorkEventArgs e)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// 为数据库节点填充子节点，如：表、视图等
        /// </summary>
        /// <param name="tviDataBase">要填充的数据库节点</param>
        /// <param name="dbName">数据库名称</param>
        void FillDataBaseNode(TreeViewItem tviDataBase, string dbName)
        {            
            string serverName = GetServerName(tviDataBase);
            List<string> names = dbBll.GetTablesName(serverName, dbName);
            if (names != null)
            {
                TreeViewItem tvi = null;
                TreeViewItem tviParent = tviDataBase.Items[0] as TreeViewItem;
                foreach (var name in names)
                {
                    tvi = new TreeViewItem();
                    tvi.Header = name;
                    tviParent.Items.Add(tvi);
                }
            }
        }

        /// <summary>
        /// 获取传入节点所属的服务器的名称
        /// </summary>
        /// <param name="tvi">包含于某一服务器节点下的 TreeViewItem</param>
        /// <returns>服务器的名称</returns>
        string GetServerName(TreeViewItem tvi)
        {
            TreeViewItemTagType tvitt = GetTagType(tvi);
            string serverName = null;
            if (tvitt == TreeViewItemTagType.Server)
            {
                serverName = tvi.Header.ToString();
            }
            else
            {
                serverName = GetServerName(tvi.Parent as TreeViewItem);
            }
            return serverName;
        }

        /// <summary>
        /// 获取传入的 TreeViewItem 对象的 Tag 属性中保存的
        /// TreeViewItemTagType 枚举的值
        /// </summary>
        /// <param name="tvi">需要判断其 Tag 属性的 TreeViewItem</param>
        /// <returns>Tag 表示的 TreeViewItemTagType 枚举值</returns>
        TreeViewItemTagType GetTagType(TreeViewItem tvi)
        {
            TreeViewItemTagType tvitt = TreeViewItemTagType.Null;
            if (tvi.Tag != null)
                tvitt = (TreeViewItemTagType)Enum.Parse(typeof(TreeViewItemTagType), tvi.Tag.ToString());
            return tvitt;
        }

        private void MenuItem_Click(object sender, RoutedEventArgs e)
        {
            MenuItem mi = sender as MenuItem;
            ContextMenu cm = mi.Parent as ContextMenu;

            SimpleFloatWindow sfWnd = new SimpleFloatWindow() { ServerName = sName, DataBaseName = dbName };
            sfWnd.Show();
        }
    }
}
